﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows.Input;
using System.Windows;

namespace Kokomo
{
	public class RuntimeEventBinding<TEventArgs> : EventBinding
		where TEventArgs : EventArgs
	{
		public RuntimeEventBinding()
		{
		}



		public string EventName
		{
			get { return (string)GetValue(EventNameProperty); }
			set { SetValue(EventNameProperty, value); }
		}

		// Using a DependencyProperty as the backing store for EventName.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty EventNameProperty =
			DependencyProperty.Register("EventName", typeof(string), typeof(RuntimeEventBinding<TEventArgs>), new UIPropertyMetadata(null));


		public override void Attach(System.Windows.DependencyObject target)
		{
			if (this.EventName != null)
			{
				EventInfo eventInfo = target.GetType().GetEvent(this.EventName);
				if (eventInfo == null) throw new InvalidOperationException(string.Format("Unknown event {0}", this.EventName));
				//if (eventInfo.EventHandlerType != typeof(EventHandler)) throw new InvalidOperationException(string.Format("Cannot attach to event {0}", this.EventName));

				MethodInfo handleMethod = ((EventHandler<TEventArgs>)this.Handle).Method;
				Delegate eventHandler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, handleMethod, false);

				eventInfo.AddEventHandler(target, eventHandler);
			}
		}
		public override void Detach(DependencyObject target)
		{
			if (this.EventName != null)
			{
				EventInfo eventInfo = target.GetType().GetEvent(this.EventName);
				if (eventInfo == null) throw new InvalidOperationException(string.Format("Unknown event {0}", this.EventName));
				//if (eventInfo.EventHandlerType != typeof(EventHandler)) throw new InvalidOperationException(string.Format("Cannot attach to event {0}", this.EventName));

				MethodInfo handleMethod = ((EventHandler<TEventArgs>)this.Handle).Method;
				Delegate eventHandler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, handleMethod, false);

				eventInfo.RemoveEventHandler(target, eventHandler);
			}
		}
		//public EventHandler HandlerDelegate { get; private set; }
		private void Handle(object target, TEventArgs e)
		{
			ICommand command = this.Command as ICommand;
			if (command != null)
			{
				if (command.CanExecute(this.CommandParameter))
				{

					if (this.FilterEvent(target, e))
					{
						command.Execute(this.CommandParameter);
						this.OnEventHandled(target, e);
					}
				}
			}
		}

		protected virtual bool FilterEvent(object target, TEventArgs e)
		{
			return true;
		}
		protected virtual void OnEventHandled(object target, TEventArgs e)
		{
			RoutedEventArgs routedArgs = e as RoutedEventArgs;
			if (routedArgs != null)
				routedArgs.Handled = true;
		}

		protected override System.Windows.Freezable CreateInstanceCore()
		{
			return new RuntimeEventBinding<TEventArgs>();
		}
	}
	public class RuntimeEventBinding : RuntimeEventBinding<EventArgs>
	{
		protected override System.Windows.Freezable CreateInstanceCore()
		{
			return new RuntimeEventBinding();
		}
	}
	public class MouseEventBinding : RuntimeEventBinding<MouseEventArgs>
	{
		protected override System.Windows.Freezable CreateInstanceCore()
		{
			return new MouseEventBinding();
		}
	}
	public class MouseDoubleClickEventBinding : RuntimeEventBinding<MouseButtonEventArgs>
	{
		static MouseDoubleClickEventBinding()
		{
			EventNameProperty.OverrideMetadata(typeof(MouseDoubleClickEventBinding), new UIPropertyMetadata("MouseLeftButtonDown"));
		}
		public MouseDoubleClickEventBinding()
		{
		}
		protected override Freezable CreateInstanceCore()
		{
			return new MouseDoubleClickEventBinding();
		}
		protected override bool FilterEvent(object target, MouseButtonEventArgs e)
		{
			return (e.ClickCount == 2);
		}
		protected override void OnEventHandled(object target, MouseButtonEventArgs e)
		{
			base.OnEventHandled(target, e);
		}
	}
}
